# -*- coding: utf-8 -*-

import webapp2
from webapp2_extras import sessions, security
# import cgi
import datetime
# import urllib
import jinja2
import os

from google.appengine.api import users
from google.appengine.ext import db
import models

def datetimeformat(value, format='%Y-%m-%dT%H:%M:%S%z'):
    return value.strftime(format)

jinja_environment = jinja2.Environment(
     loader=jinja2.FileSystemLoader('templates'))

# jinja_environment = jinja2.Environment(
#     loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))

# jinja_environment.globals.update(users=users)

# def get_key_id(entity):
#     return entity.key().id()
# jinja_environment.globals.update(get_key_id=get_key_id)

def escape_html_entities(s):
    table = [("&", "&amp;"), ("<", "&lt;"), (">", "&gt;"), ('"', "&quot;"), ("'", "&#39;")]
    s = str(s)
    for f, t in table:
        s.replace(f, t)
    return s

jinja_environment.globals.update(m_User=models.User, m_users=users)
jinja_environment.filters['datetimeformat'] = datetimeformat
jinja_environment.filters['e'] = escape_html_entities

def render_template(request, name, variables={}):
    template = jinja_environment.get_template(name)
    request.response.out.write(template.render(variables))

def require_login(requestHandler=None, url=None):
    current_user = models.User.get_current_user()
    if current_user is None:
        if requestHandler is None:
            webapp2.abort(401)
        else:
            if url is None: url = requestHandler.request.uri
            login_url = users.create_login_url(url)
            requestHandler.redirect(login_url)
    else:
        return current_user

def csrf_token_check(requestHandler):
    if requestHandler.request.POST['token'] != requestHandler.csrf_token:
        requestHandler.abort(400, "Invalid Request")

def require_admin():
    if not models.User.get_current_user().admin:
        webapp2.abort(401)

class BaseHandler(webapp2.RequestHandler):
    def dispatch(self):
        # If DB is empty, initialize it.
        db_check = db.Query(keys_only=True).fetch(1)
        if not db_check:
            self.redirect("/_admin/init")

        # Get a session store for this request.
        self.session_store = sessions.get_store(request=self.request)
        # Set CSRF Token
        session = self.session_store.get_session()
        if "token" not in session:
            session["token"] = security.generate_random_string(30)
        self.csrf_token = session["token"]

        try:
            # Dispatch the request.
            webapp2.RequestHandler.dispatch(self)
        finally:
            # Save all sessions.
            self.session_store.save_sessions(self.response)

class Index(BaseHandler):
    def get(self):
        items = models.Item.alive_all().order("-creation_time").run(limit=30)
        items = list(items)
        item_ids = [item.key().id() for item in items]
        seller_ids = [item.seller.key().id() for item in items]
        render_template(self, "index.html",
          {"items": items, "item_ids": item_ids, "seller_ids": seller_ids})

class Item(BaseHandler):
    def get(self, key):
        item = models.Item.get_by_id(int(key))
        
        current_user = models.User.get_current_user()
        if (item is None) or \
        (item.is_expired() and \
         (not current_user or \
          not (item.seller == current_user or current_user.admin))):
            self.abort(404)
        elif item.seller == current_user.user and not item.is_expired() \
                 and self.request.get("mode") != "view":
            render_template(self, "item_post.html",
                            {"item": item, "token": self.csrf_token})
        else:
            render_template(self, "item.html", {"item": item})

    def add(self):
        require_login(self, "/item/add")
        render_template(self, "item_post.html",
                        {"token": self.csrf_token})

    def post(self, key=None):
        require_login(self, "/") # todo: set correct url
        csrf_token_check(self)
        
        current_user = users.get_current_user()
        if "id" in self.request.POST:
            # edit item
            _id = int(self.request.POST['id'])
            item = models.Item.get_by_id(_id)
            if item is None:
                self.abort(403, "Item {0} is not found.".format(_id))
            if item.seller.user != current_user:
                self.abort(401, "Item {0} is not your item. You can only edit your items.".format(_id))
        else: # add new item
            current_user = models.User.get_current_user()
            item = models.Item(seller=current_user, parent=current_user)

        # [[property_name, is_required], ...]
        props = [["title", True, str], ["description", False, str], ["price", True, int]]
        for prop, required, _filter in props:
            val = self.request.get(prop).strip()
            if val:
                setattr(item, prop, _filter(self.request.POST[prop]))
            else:
                if required: self.abort(400, "{0} is required.".format(prop))
        item.put()
        self.redirect("/item/{0}?mode=view".format(item.key().id()))

    def delete(self):
        require_login(self, "/")
        csrf_token_check(self)
        
        _id = int("0" + self.request.get("id"))
        if not _id: self.abort(401)
        item = models.Item.get_by_id(_id)
        if item is None: self.abort(400, "Item {0} is not exist.")
        current_user = models.User.get_current_user()
        if item.seller != current_user:
            self.abort(401, "Item {0} is not yours.".format(_id))
        item.delete()
        self.redirect("/user/{0}".format(current_user.key().id()))

class User(BaseHandler):
    def get(self, key):
        current_user = models.User.get_current_user()
        user = models.User.get_by_id(int(key))
        if current_user == user:
            render_template(self, "user_edit.html",
                            {"user": user, "token": self.csrf_token})
        else:
            items = models.Item.alive_all().filter("seller =", user)
            render_template(self, "user.html", {"user": user, "items": items})

    def post(self):
        user = require_login(self, "/")
        csrf_token_check(self)

        for prop in ['name', 'description']:
            setattr(user, prop, self.request.get(prop))

        self.redirect("/user/{0}".format(user.key().id()))

    def delete(self):
        current_user = require_login(self)
        csrf_token_check(self)
        
        db.delete(db.Query(keys_only=True).ancestor(current_user))
        # self.response.out.write("Your data has been deleted.")
        self.redirect(users.create_logout_url(""))

class Dev(BaseHandler):
    def expired_items(self):
        models.Item.expired_all()

    def test(self):
        self.response.out.write(models.User.all().filter("user =", users.get_current_user()).get().admin)
        self.response.out.write("<hr>")
        self.response.out.write(models.User.get_current_user().admin)

class Admin(BaseHandler):
    def init(self):
        models.initialize_storage(True)
        self.response.out.write("""
<html>
<body>
<p>Welcome. DB is initialized.
<p>Go to <a href="/">index</a>
</body>
</html>""")

    def index(self):
        require_admin()
        render_template(self, "admin/index.html", {"token": self.csrf_token})

    def deactivate_user(self):
        require_admin()
        csrf_token_check(self)
        
        _id = int(self.request.POST['id'])
        user = models.User.get_by_id(_id)
        if not user.admin:
            user.deactivated = True
            user.put()
            self.response.out.write("deactivated user {0}".format(_id))
        else:
            self.abort(400, "You can't deactivate an administrator.")

    def reactivate_user(self):
        require_admin()
        csrf_token_check(self)
        
        _id = int(self.request.POST['id'])
        user = models.User.get_by_id(_id)
        if user.deactivated:
            user.deactivated = False
            user.put()
            self.response.out.write("reactivated user {0}".format(_id))
        else:
            self.abort(400, "User {0} is not deactivated.".format(_id))

    def delete_item(self):
        require_admin()
        csrf_token_check(self)
        
        _id = int(self.request.POST['id'])
        models.Item.get_by_id(_id).delete()
        self.response.out.write("deleted")

    def add_admin(self):
        require_admin()
        csrf_token_check(self)
        
        email = self.request.POST['email']
        user = models.User.create(email)
        user.admin = True
        user.put()
        self.response.out.write("added")

def custom_dispatcher(router, request, response):
    # account
    current_user = users.get_current_user()
    model_current_user = models.User.get_current_user()
    if current_user and not model_current_user:
        models.User.create().put()

    # deactivate user
    if model_current_user and model_current_user.deactivated:
        webapp2.abort(403, "Your account has been deactivated.")
    
    return router.default_dispatcher(request, response)

app = webapp2.WSGIApplication([
    webapp2.Route('/_admin/', handler=Admin, handler_method="index"),
    webapp2.Route('/_admin/init', handler=Admin, handler_method="init"),
    webapp2.Route('/_admin/deactivate_user', handler=Admin, handler_method="deactivate_user", methods=['POST']),
    webapp2.Route('/_admin/delete_item', handler=Admin, handler_method="delete_item", methods=['POST']),
    webapp2.Route('/_admin/add_admin', handler=Admin, handler_method="add_admin", methods=['POST']),

    webapp2.Route('/_dev/test', handler=Dev, handler_method="test"),
    webapp2.Route('/_dev/expired_items', handler=Dev, handler_method="expired_items"),

    webapp2.Route('/', handler=Index, name="index"),
    webapp2.Route('/user/delete', handler=User, name="user.delete", handler_method="delete", methods=['POST']),
    webapp2.Route('/user/<key>', handler=User, name="user"),
    webapp2.Route('/item/add', handler=Item, name="item.add", handler_method="add"),
    webapp2.Route('/item/delete', handler=Item, name="item.delete", handler_method="delete", methods=['POST']),
    webapp2.Route('/item/<key>', handler=Item, name="item"),
    webapp2.Route('/item', handler=Item, methods=['POST']),],
    debug=True,
    config={'webapp2_extras.sessions': {
      'secret_key': 'AoDOasNvAsg9RskK',
    }})

app.router.set_dispatcher(custom_dispatcher)
